using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using ClimbTracker.BusinessLogic.Entities;
using System.IO;
using Emgu.CV;
using ClimbTracker.GUI.Utils;
using MySql.Data.MySqlClient;
using System.Collections;

namespace ClimbTracker.GUI.TabControls
{
    public partial class RoutesTabControl : UserControl
    {

        // Constant used for mouse events over labels
        private const int EPSILON = 5;

        // Keep track of when fake drag or resize mode is enabled.
        private bool isDragging = false;
        private bool isResizing = false;
        private bool shouldDrawBoundary = false;
        private bool shouldMoveBoundary = false;
        private bool isEditingRoute = false;
        private bool wallMarkingsChanged = false;
        private ClimbTrackerDataSet.routesRow selectedRow = null;
        private IComparer<HotSpot> hotSpotComparer = new HotspotComparer();

        private static HotSpot[] verticalBoundaries = new HotSpot[2];
        private int verticalBoundaryIndex = 0;
        private int boundaryCount = 0;

        // Store the location where the user clicked on the control.
        private int clickOffsetX, clickOffsetY;
        private int selectedRowIndex = 0;

        private Color selectedColor = Color.Blue;
        private ActiveButton activeButton = ActiveButton.DrawHolds;

        // Event handler objects
        private MouseEventHandler routeImageBoxMouseMoveHandler = null;
        private MouseEventHandler routeImageBoxMouseDownHandler = null;
        private MouseEventHandler routeImageBoxMouseUpHandler = null;
        private MouseEventHandler routeImageBoxMouseDoubleClickHandler = null;

        private static List<HotSpot> _hotspotControlsList = new List<HotSpot>();
        private static Route _selectedRoute = new Route();
        private Route _currentRoute = new Route();
        private static Capture _routeCapture;
        private static int _rotationAngle = 270;

        private Image<Bgr, Byte> currentFrame = null;

        public static List<HotSpot> HotspotControlsList
        {
            get { return RoutesTabControl._hotspotControlsList; }
            set { RoutesTabControl._hotspotControlsList = value; }
        }
        public static Route SelectedRoute
        {
            get { return RoutesTabControl._selectedRoute; }
            set { RoutesTabControl._selectedRoute = value; }
        }
        public Route CurrentRoute
        {
            get { return _currentRoute; }
            set { _currentRoute = value; }
        }
        public static Capture RouteCapture
        {
          get { return RoutesTabControl._routeCapture; }
          set { RoutesTabControl._routeCapture = value; }
        }
        public static int RotationAngle
        {
            get { return _rotationAngle; }
            set { _rotationAngle = value; }
        }


        public RoutesTabControl()
        {
            InitializeComponent();
            // TODO: This line of code loads data into the 'climbTrackerDataSet.routes' table. You can move, or remove it, as needed.
            this.routesTableAdapter.Fill(this.climbTrackerDataSet.routes);
            this.wallinstallationsTableAdapter.Fill(this.climbTrackerDataSet.wallinstallations);

            //Initialize event handlers
            routeImageBoxMouseMoveHandler = new System.Windows.Forms.MouseEventHandler(this.routeImageBox_MouseMove);
            routeImageBoxMouseDownHandler = new System.Windows.Forms.MouseEventHandler(this.routeImageBox_MouseDown);
            routeImageBoxMouseUpHandler = new System.Windows.Forms.MouseEventHandler(this.routeImageBox_MouseUp);
            routeImageBoxMouseDoubleClickHandler = new System.Windows.Forms.MouseEventHandler(this.routeImageBox_MouseDoubleClick);

            // Fix noImageLabelLocation
            this.Controls.Remove(noImageLabel);
            this.noImageLabel.Location = new System.Drawing.Point(30, 200);
            this.routeImageBox.Controls.Add(noImageLabel);

        }




        private void selectForTrackingButton_Click(object sender, EventArgs e)
        {
            // Update the SelectedRoute object
            CurrentRoute = new Route();
            CurrentRoute.Name = NameTextBox.Text;
            CurrentRoute.MaxScore = int.Parse(maxScoreNumericUpDown.Value.ToString());
            CurrentRoute.TimePeriodMinutes = int.Parse(timeConstraintNumericUpDown.Value.ToString());
            CurrentRoute.Difficulty = double.Parse(difficultyTextBox.Text);
            CurrentRoute.PicturePath = picturePathTextBox.Text;

            if ((verticalBoundaries[0] != null) && (verticalBoundaries[1] != null))
            {
                if (verticalBoundaries[0].Location.X <= verticalBoundaries[1].Location.X)
                {
                    CurrentRoute.LeftBoundary = verticalBoundaries[0].Location.X;
                    CurrentRoute.RightBoundary = verticalBoundaries[1].Location.X;
                }
                else
                {
                    CurrentRoute.LeftBoundary = verticalBoundaries[1].Location.X;
                    CurrentRoute.RightBoundary = verticalBoundaries[0].Location.X;
                }
            }
            else
            {
                CurrentRoute.LeftBoundary = -1;
                CurrentRoute.RightBoundary = -1;
            }

            selectForTrackingButton.Enabled = false;
            SelectedRoute = CurrentRoute;
        }


        private void addRouteButton_Click(object sender, EventArgs e)
        {
            // Update buttons' status
            changeButtonsStatus(false);
            cancelRouteButton.Enabled = true;

            // Update panels' status
            routeCardGroupBox.Enabled = true;

            // Update image box
            routeImageBox.Image = null;
            routeImageBox.Refresh();

            // Clear markings
            clearHotSpots();
            clearBoundaries();

            //CurrentRoute.WallInstallations.Clear();
            CurrentRoute = new Route();
            wallMarkingsChanged = false;

            fillBlankRouteCard();
        }


        private void deleteRouteButton_Click(object sender, EventArgs e)
        {
            ClimbTrackerDataSet.routesRow row;
            try
            {
                row = climbTrackerDataSet.routes.FindByRouteID((uint)CurrentRoute.RouteID);
                if (row == null)
                    throw new IndexOutOfRangeException();
            }
            catch (IndexOutOfRangeException ex)
            {
                MessageBox.Show("Please select a route.", "No Route selected",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            
            // Delete routes' wall installations from DB
            DataRow[] dataRows = climbTrackerDataSet.wallinstallations.Select("RouteID = " + CurrentRoute.RouteID);
            foreach (DataRow rowToDelete in dataRows)
            {
                rowToDelete.Delete();
            }
            updateDB("wallinstallations");
            
            // Delete row from DB
            row.Delete();
            updateDB("Routes");
            routeImageBox.Image = null;
            

            // Fire a cell click event on the right row
            if (selectedRowIndex != 0)
            {
                if (selectedRowIndex < (climbTrackerDataSet.routes.Count))
                {
                    routesDataGridView_CellMouseClick(null, new DataGridViewCellMouseEventArgs(1, selectedRowIndex, 0, 0, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)));
                }
                else
                {
                    selectedRowIndex = climbTrackerDataSet.routes.Count - 1;
                    routesDataGridView_CellMouseClick(null, new DataGridViewCellMouseEventArgs(1, selectedRowIndex, 0, 0, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)));
                }

                // Select the appropriate row in the datagrid
                routesDataGridView.Rows[selectedRowIndex].Selected = true;
            }
            else
            {
                // Disable edit \ delete \ select buttons and clear all route fields
                editRouteButton.Enabled = false;
                deleteRouteButton.Enabled = false;
                selectForTrackingButton.Enabled = false;
                idContentLabel.Text = "";
                NameTextBox.Text = "";
                maxScoreNumericUpDown.Value = 0;
                timeConstraintNumericUpDown.Value = 0;
                difficultyTextBox.Text = "";
                picturePathTextBox.Text = "";
                routeImageBox.Refresh();
            }
        }


        private void saveRouteButton_Click(object sender, EventArgs e)
        {
            if (!isMissingFields())
            {

                // In case only one vertical border was drawn, deny the save action
                switch (boundaryCount)
                {
                    case 0:
                        CurrentRoute.LeftBoundary = -1;
                        CurrentRoute.RightBoundary = -1;
                        break;
                    case 1:
                        MessageBox.Show("You must draw exactly 0 or 2 vertical borders.", "Only one vertical border",
                                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    case 2:
                        if (CurrentRoute.LeftBoundary <= CurrentRoute.RightBoundary)
                        {
                            CurrentRoute.LeftBoundary = verticalBoundaries[0].Location.X;
                            CurrentRoute.RightBoundary = verticalBoundaries[1].Location.X;
                        }
                        else
                        {
                            CurrentRoute.LeftBoundary = verticalBoundaries[1].Location.X;
                            CurrentRoute.RightBoundary = verticalBoundaries[0].Location.X;
                        }
                        break;
                }


                // If any wall installations were added \ changed \ removed, update the DB
                if (wallMarkingsChanged)
                {

                    // Delete previous wall installations from DB
                    DataRow[] dataRows = climbTrackerDataSet.wallinstallations.Select("RouteID = " + CurrentRoute.RouteID);
                    foreach (DataRow row in dataRows)
                    {
                        row.Delete();
                    }
                    updateDB("wallinstallations");

                    // Resort the items
                    sortHotSpots();

                    // Add new wall installations to DB
                    foreach (WallInstallation wi in CurrentRoute.WallInstallations)
                    {
                        addNewWallInstallationRowToDataSet(wi);
                        updateDB("wallinstallations");
                    }
                }

                if (isEditingRoute)
                {
                    updateRouteRowInDataGridView();
                }
                else
                {
                    CurrentRoute.StartFrame = 1;
                    addNewRouteRowToDataGridView();
                    selectedRowIndex++;
                }

                // Update panel's status
                changePanelsEnabled(false);
                picturePathTextBox.Enabled = false;
                routeImageBox.Cursor = Cursors.Default;

                // Update buttons' status
                drawHoldsButton.Enabled = true;
                drawHoldsButton.FlatStyle = FlatStyle.Standard;
                drawVerticalBoundariesButton.Enabled = true;
                drawVerticalBoundariesButton.FlatStyle = FlatStyle.Standard;
                drawVerticalBoundariesButton.Enabled = true;
                captureFrameButton.Visible = false;
                changeButtonsStatus(true);

                updateDB("Routes");

                removeRouteImageBoxEventHandlers();

                if (!isEditingRoute)
                {
                    routesDataGridView_CellMouseClick(null, new DataGridViewCellMouseEventArgs(1, routesDataGridView.Rows.Count - 1, 0, 0, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)));
                }
                else
                {

                    isEditingRoute = false;
                }
                wallMarkingsChanged = false;
            }
        }



        private void editRouteButton_Click(object sender, EventArgs e)
        {
            // Update buttons' status
            changeButtonsStatus(false);

            // Update panels' status
            routeCardGroupBox.Enabled = true;
            picturePathTextBox.Enabled = true;
            if (RouteCapture != null)
            {
                routeEditingGroupBox.Enabled = true;
                if (CurrentRoute.WallInstallations.Count > 0)
                {
                    clearButton.Enabled = true;
                }
                routeImageBox.Enabled = true;
            }

            // Raise editing flag
            isEditingRoute = true;
        }


        private void cancelRouteButton_Click(object sender, EventArgs e)
        {
            // Update buttons' status
            drawHoldsButton.Enabled = true;
            drawHoldsButton.FlatStyle = FlatStyle.Standard;
            drawVerticalBoundariesButton.Enabled = true;
            drawVerticalBoundariesButton.FlatStyle = FlatStyle.Standard;
            captureFrameButton.Visible = false;
            changeButtonsStatus(true);            

            // Update panels' status
            changePanelsEnabled(false);
            routeImageBox.Cursor = Cursors.Default;

            isEditingRoute = false;

            // Fill routes card and details
            fillRouteDetails(CurrentRoute);
            fillRouteCard();

            routeImageBox.Controls.Clear();
            verticalBoundaries[0] = null;
            verticalBoundaries[1] = null;
            HotspotControlsList.Clear();
            drawHotspots();
            drawVerticalBoundaries();

            removeRouteImageBoxEventHandlers();

            routesDataGridView_CellMouseClick(null, new DataGridViewCellMouseEventArgs(1, selectedRowIndex, 0, 0, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)));
        }



        /// <summary>
        /// Update the database with all table changes.
        /// This will function makes the database table reflect the dataGridView.
        /// </summary>
        /// <param name="table"></param>
        public void updateDB(String table)
        {
            MySqlConnection myConnection = new MySqlConnection("server=localhost;user id=root;database=climbtracker");
            MySqlDataAdapter mySqlDataAdapter = new MySqlDataAdapter("Select * from " + table, myConnection);
            MySqlCommandBuilder mySqlCommandBuilder = new MySqlCommandBuilder(mySqlDataAdapter);
            mySqlDataAdapter.Update(climbTrackerDataSet, table);
        }


        private void addNewRouteRowToDataGridView()
        {
            ClimbTrackerDataSet.routesRow newRouteRow = climbTrackerDataSet.routes.NewroutesRow();
            newRouteRow.Name = NameTextBox.Text;
            newRouteRow.MaxScore = (byte)maxScoreNumericUpDown.Value;
            newRouteRow.TimePeriodMinutes = (byte)timeConstraintNumericUpDown.Value;
            newRouteRow.Difficulty = double.Parse(difficultyTextBox.Text);
            newRouteRow.PicturePath = picturePathTextBox.Text;
            newRouteRow.LeftBoundary = CurrentRoute.LeftBoundary;
            newRouteRow.RightBoundary = CurrentRoute.RightBoundary;
            newRouteRow.StartFrame = (uint)CurrentRoute.StartFrame;
            newRouteRow.RotationAngle = (ushort)CurrentRoute.RotationAngle;
            climbTrackerDataSet.routes.RouteIDColumn.AllowDBNull = true;
            climbTrackerDataSet.routes.AddroutesRow(newRouteRow);
        }


        /// <summary>
        /// Updates the routes' dataGridView with the details of the last selected competitor.
        /// </summary>
        private void updateRouteRowInDataGridView()
        {
            selectedRow.Name = NameTextBox.Text;
            selectedRow.MaxScore = (byte)maxScoreNumericUpDown.Value;
            selectedRow.TimePeriodMinutes = (byte)timeConstraintNumericUpDown.Value;
            selectedRow.Difficulty = double.Parse(difficultyTextBox.Text);
            selectedRow.PicturePath = picturePathTextBox.Text;
            selectedRow.LeftBoundary = CurrentRoute.LeftBoundary;
            selectedRow.RightBoundary = CurrentRoute.RightBoundary;
            selectedRow.RotationAngle = (ushort)CurrentRoute.RotationAngle;
            climbTrackerDataSet.routes.RouteIDColumn.AllowDBNull = true;
        }


        /// <summary>
        /// Validates that all route fields are full and valid. Returns false if one of the fields
        /// is missing \ incorrect and true otherwise.
        /// </summary>
        /// <returns></returns>
        private bool isMissingFields()
        {
            // Accumulate the missing fields
            String emptyFields = "";
            if (NameTextBox.Text.Trim().Length == 0)
            {
                emptyFields += "Name, ";
            }
            if (timeConstraintNumericUpDown.Value == 0)
            {
                emptyFields += "Time Constraint, ";
            }

            try
            {
                double testParse = double.Parse(difficultyTextBox.Text);
            }
            catch
            {
                MessageBox.Show("Difficulty must have a numeric value", "Invalid field", 
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return true;
            }
            //if (difficultyTextBox.Text.Trim().Length == 0)
            //{
            //    emptyFields += "Difficulty, ";
            //}

            if (emptyFields.Length > 0)
            {
                emptyFields.Trim();
                emptyFields = emptyFields.Substring(0, emptyFields.Length - 2);

                MessageBox.Show("You must fill in the following fields:\n" + emptyFields, "Missing Fields",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return true;
            }


            return false;
        }



        /// <summary>
        /// Fills the given route object with details from the selected row.
        /// </summary>
        /// <param name="route"></param>
        private void  fillRouteDetails(Route route)
        {
            try
            {
                route.RouteID = int.Parse(routesDataGridView.Rows[selectedRowIndex].Cells["routeIDDataGridViewTextBoxColumn"].Value.ToString());
            }
            catch
            {
                route.RouteID = 0;
            }
            ClimbTrackerDataSet.routesRow routeRow = climbTrackerDataSet.routes.FindByRouteID((uint)route.RouteID);
            route.Name = routesDataGridView.Rows[selectedRowIndex].Cells["nameDataGridViewTextBoxColumn"].Value.ToString();
            route.MaxScore = int.Parse(routesDataGridView.Rows[selectedRowIndex].Cells["maxScoreDataGridViewTextBoxColumn"].Value.ToString());
            route.TimePeriodMinutes = int.Parse(routesDataGridView.Rows[selectedRowIndex].Cells["timePeriodMinutesDataGridViewTextBoxColumn"].Value.ToString());
            route.Difficulty = double.Parse(routesDataGridView.Rows[selectedRowIndex].Cells["difficultyDataGridViewTextBoxColumn"].Value.ToString());
            route.PicturePath = routesDataGridView.Rows[selectedRowIndex].Cells["picturePathDataGridViewTextBoxColumn"].Value.ToString();
            route.StartFrame = (int)routeRow.StartFrame;
            route.LeftBoundary = routeRow.LeftBoundary;
            route.RightBoundary = routeRow.RightBoundary;
            route.RotationAngle = routeRow.RotationAngle;
            route.WallInstallations.Clear();
        }



        /// <summary>
        /// Creates a card with default details.
        /// </summary>
        private void fillBlankRouteCard()
        {
            // Fill the text \ numeric fields
            NameTextBox.Text = "";
            maxScoreNumericUpDown.Value = 25;
            timeConstraintNumericUpDown.Value = 1;
            difficultyTextBox.Text = "";
            picturePathTextBox.Text = "";
        }



        /// <summary>
        /// Fills the selected comptitor's details in the file card.
        /// </summary>
        private void fillRouteCard()
        {
            // Fill the text \ numeric fields
            idContentLabel.Text = CurrentRoute.RouteID.ToString();
            NameTextBox.Text = CurrentRoute.Name;
            maxScoreNumericUpDown.Value = CurrentRoute.MaxScore;
            timeConstraintNumericUpDown.Value = CurrentRoute.TimePeriodMinutes;
            difficultyTextBox.Text = CurrentRoute.Difficulty.ToString();
            picturePathTextBox.Text = CurrentRoute.PicturePath;
            picturePathTextBox.Enabled = false;
        }

        /// <summary>
        /// Gets the first frame of the selected route's video and displays it in the picture box
        /// </summary>
        private void updateImageBoxWithFirstFrame()
        {
            // Assign the first frame from the video to the picture box
            try
            {
                // Cleanup previous capture and get the new one
                RouteCapture = null;
                RouteCapture = new Capture(picturePathTextBox.Text);
                
                currentFrame = RouteCapture.QueryFrame(); //reduce noise from the image
                rotateFrame();
                routeImageBox.Controls.Remove(noImageLabel);
                CurrentRoute.LiveCapture = false;
            }
            catch (Exception ex)
            {
                routeImageBox.Controls.Add(noImageLabel);
                routeImageBox.Image = null;
                //routeImageBox.Enabled = false;
                //routeEditingGroupBox.Enabled = false;
                RouteCapture = new Capture();
                CurrentRoute.LiveCapture = true;
                routeImageBox.Image = null;
            }

        }

        /// <summary>
        /// Rotate the frame displayed from the route video by the assigned 'RotationAngle' degrees
        /// </summary>
        private void rotateFrame()
        {
            routeImageBox.Image = currentFrame.Rotate(CurrentRoute.RotationAngle, new Bgr(Color.Black), false);
            routeImageBox.Refresh();
        }



        private void routesDataGridView_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            // If the selected cell is in the header row, or if the routes table is empty, ignore
            if (e.RowIndex == -1 || climbTrackerDataSet.routes.Count == 0)
                return;

            clearHotSpots();
            clearBoundaries();

            selectedRowIndex = e.RowIndex;
            selectedRow = (ClimbTrackerDataSet.routesRow)climbTrackerDataSet.routes.Rows[selectedRowIndex];

            // Fill the local route object with the details.
            // This object will be used to fill in the form details and also expose them
            // to the other tabs in the application.
            fillRouteDetails(CurrentRoute);
            fillRouteCard();
            drawHotspots();
            drawVerticalBoundaries();

            // Update buttons status
            changeButtonsStatus(true);

            updateImageBoxWithFirstFrame();

            // Check if we're using a live capture. If so, query the first frame and show recapture button
            if (CurrentRoute.LiveCapture == true)
            {
                captureFrameButton.Visible = true;
                if (CurrentRoute.WallInstallations.Count > 0 || CurrentRoute.LeftBoundary > -1)
                {
                    routeImageBox.Image = RouteCapture.QueryFrame();
                }
                else
                {
                    routeImageBox.Image = null;
                }
            }
            else
            {
                captureFrameButton.Visible = false;
            }

            // Update panels' status
            changePanelsEnabled(false);
            routeImageBox.Refresh();
            
        }



        private void clearBoundaries()
        {
            if (boundaryCount >= 1)
            {
                routeImageBox.Controls.Remove(verticalBoundaries[0]);

                if (boundaryCount == 2)
                {
                    routeImageBox.Controls.Remove(verticalBoundaries[1]);
                }
            }

            verticalBoundaries[0] = null;
            verticalBoundaries[1] = null;
            boundaryCount = 0;
            verticalBoundaryIndex = 0;

            CurrentRoute.LeftBoundary = -1;
            CurrentRoute.LeftBoundary = -1;
            routeImageBox.Refresh();
        }


        private void clearHotSpots()
        {
            foreach (HotSpot hotspot in HotspotControlsList)
            {
                routeImageBox.Controls.Remove(hotspot);
            }

            HotspotControlsList.Clear();
            maxScoreNumericUpDown.Value = 0;
            routeImageBox.Refresh();
        }


        /// <summary>
        /// Populate the vertical boundaries of the current route from the DB and draw them
        /// </summary>
        private void drawVerticalBoundaries()
        {
            boundaryCount = 0;
            shouldDrawBoundary = false;

            if (!(CurrentRoute.LeftBoundary == -1 || CurrentRoute.RightBoundary == -1))
            {
                boundaryCount++;
                verticalBoundaryIndex = boundaryCount - 1;

                // Create new hotspot object that will function as a boundary
                verticalBoundaries[verticalBoundaryIndex] = new HotSpot();
                verticalBoundaries[verticalBoundaryIndex].Size = new Size(5, routeImageBox.Height);
                verticalBoundaries[verticalBoundaryIndex].ForeColor = holdColorContentLabel.BackColor;
                verticalBoundaries[verticalBoundaryIndex].BackColor = holdColorContentLabel.BackColor;
                verticalBoundaries[verticalBoundaryIndex].Cursor = Cursors.SizeAll;
                verticalBoundaries[verticalBoundaryIndex].Location = new Point(CurrentRoute.LeftBoundary, 0);
                verticalBoundaries[verticalBoundaryIndex].MouseDoubleClick += new MouseEventHandler(boundary_MouseDoubleClick);
                verticalBoundaries[verticalBoundaryIndex].MouseDown += new MouseEventHandler(boundary_MouseDown);
                verticalBoundaries[verticalBoundaryIndex].MouseMove += new MouseEventHandler(boundary_MouseMove);
                verticalBoundaries[verticalBoundaryIndex].MouseUp += new MouseEventHandler(boundary_MouseUp);
                routeImageBox.Controls.Add(verticalBoundaries[verticalBoundaryIndex]);

                boundaryCount++;
                verticalBoundaryIndex = boundaryCount - 1;

                // Create new hotspot object that will function as a boundary
                verticalBoundaries[verticalBoundaryIndex] = new HotSpot();
                verticalBoundaries[verticalBoundaryIndex].Size = new Size(5, routeImageBox.Height);
                verticalBoundaries[verticalBoundaryIndex].ForeColor = holdColorContentLabel.BackColor;
                verticalBoundaries[verticalBoundaryIndex].BackColor = holdColorContentLabel.BackColor;
                verticalBoundaries[verticalBoundaryIndex].Cursor = Cursors.SizeAll;
                verticalBoundaries[verticalBoundaryIndex].Location = new Point(CurrentRoute.RightBoundary, 0);
                verticalBoundaries[verticalBoundaryIndex].MouseDoubleClick += new MouseEventHandler(boundary_MouseDoubleClick);
                verticalBoundaries[verticalBoundaryIndex].MouseDown += new MouseEventHandler(boundary_MouseDown);
                verticalBoundaries[verticalBoundaryIndex].MouseMove += new MouseEventHandler(boundary_MouseMove);
                verticalBoundaries[verticalBoundaryIndex].MouseUp += new MouseEventHandler(boundary_MouseUp);
                routeImageBox.Controls.Add(verticalBoundaries[verticalBoundaryIndex]);

                shouldDrawBoundary = false;
            }

        }



        /// <summary>
        /// Populate the hotspots list of the current route from the DB and draw them
        /// </summary>
        private void drawHotspots()
        {
            foreach (ClimbTrackerDataSet.wallinstallationsRow row in climbTrackerDataSet.wallinstallations)
            {
                if (row.RouteID == CurrentRoute.RouteID)
                {
                    // Create and configure the shape with some defaults.
                    HotSpot newHotSpot = new HotSpot();
                    newHotSpot.Size = new Size(30, 30);
                    newHotSpot.ForeColor = holdColorContentLabel.BackColor; //TODO: getcolor from DB


                    // Add the control to the hotspots collections
                    HotspotControlsList.Add(newHotSpot);
                    newHotSpot.InnerText = HotspotControlsList.Count.ToString();
                    newHotSpot.Location = new Point((int)row.XCoordinate, (int)row.YCoordinate);  // this.PointToClient(Control.MousePosition);
                    newHotSpot.Y = (int)row.YCoordinate;

                    // Connect the shape to all its event handlers.
                    newHotSpot.MouseDown += new MouseEventHandler(label_MouseDown);
                    newHotSpot.MouseMove += new MouseEventHandler(label_MouseMove);
                    newHotSpot.MouseUp += new MouseEventHandler(label_MouseUp);
                    newHotSpot.MouseDoubleClick += new MouseEventHandler(label_MouseDoubleClick);

                    // Add the shape to the form.
                    routeImageBox.Controls.Add(newHotSpot);

                    // Add the hotspot to the route's wall installations set
                    newHotSpot.WallInstallation = new WallInstallation((int)row.XCoordinate, (int)row.YCoordinate, holdColorContentLabel.BackColor.ToString(), WallInstallation.HOLD, CurrentRoute.RouteID);
                    CurrentRoute.WallInstallations.Add(newHotSpot.WallInstallation);
                }

                sortHotSpots();
            }
        }



        private void routeImageBox_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            switch (activeButton)
            {
                case ActiveButton.DrawClips:
                    break;

                case ActiveButton.DrawHolds:
                    routeImageBox.Cursor = Cursors.Default;

                    // Create and configure the shape with some defaults.
                    HotSpot newHotSpot = new HotSpot();
                    newHotSpot.Size = new Size(30, 30);
                    newHotSpot.ForeColor = holdColorContentLabel.BackColor;


                    // Add the control to the hotspots collections
                    HotspotControlsList.Add(newHotSpot);
                    newHotSpot.InnerText = HotspotControlsList.Count.ToString();

                    // To determine where to place the shape, you need to convert the 
                    // current screen-based mouse coordinates into relative form coordinates.
                    int XCoordinate = e.X - (newHotSpot.Width / 2);
                    int YCoordinate = e.Y - (newHotSpot.Height / 2);
                    newHotSpot.Location = new Point(XCoordinate, YCoordinate);  // this.PointToClient(Control.MousePosition);
                    newHotSpot.Y = YCoordinate;

                    // Connect the shape to all its event handlers.
                    newHotSpot.MouseDown += new MouseEventHandler(label_MouseDown);
                    newHotSpot.MouseMove += new MouseEventHandler(label_MouseMove);
                    newHotSpot.MouseUp += new MouseEventHandler(label_MouseUp);
                    newHotSpot.MouseDoubleClick += new MouseEventHandler(label_MouseDoubleClick);

                    // Add the shape to the form.
                    routeImageBox.Controls.Add(newHotSpot);

                    // Add the hotspot to the route's wall installations set
                    newHotSpot.WallInstallation = new WallInstallation(XCoordinate, YCoordinate, holdColorContentLabel.BackColor.ToString(), WallInstallation.HOLD, CurrentRoute.RouteID);
                    CurrentRoute.WallInstallations.Add(newHotSpot.WallInstallation);

                    maxScoreNumericUpDown.Value = HotspotControlsList.Count;
                    wallMarkingsChanged = true;
                    saveRouteButton.Enabled = true;
                    break;

            }
        }


        private void addNewWallInstallationRowToDataSet(WallInstallation wallInstallation)
        {
            ClimbTrackerDataSet.wallinstallationsRow wallInstallationRow = climbTrackerDataSet.wallinstallations.NewwallinstallationsRow();
            wallInstallationRow.XCoordinate = (uint)wallInstallation.XCoordinate;
            wallInstallationRow.YCoordinate = (uint)wallInstallation.YCoordinate;
            wallInstallationRow.Color = wallInstallation.Color;
            wallInstallationRow.Type = wallInstallation.Type;
            wallInstallationRow.RouteID = (uint)wallInstallation.RouteID;
            climbTrackerDataSet.wallinstallations.NameColumn.AllowDBNull = true;
            climbTrackerDataSet.wallinstallations.HeightColumn.AllowDBNull = true;
            climbTrackerDataSet.wallinstallations.InstallationIDColumn.AllowDBNull = true;
            climbTrackerDataSet.wallinstallations.AddwallinstallationsRow(wallInstallationRow);
        }


        /// <summary>
        /// Determines during a mouse down event whether the user is resizing or dragging the hotspot
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void label_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            // Retrieve a reference to the active label.
            Control currentControl;
            currentControl = (Control)sender;

            if (e.Button == MouseButtons.Left)
            {
                clickOffsetX = e.X;
                clickOffsetY = e.Y;

                if (e.X < EPSILON || e.Y < EPSILON || currentControl.Width - e.X < EPSILON || currentControl.Height - e.Y < EPSILON)
                {
                    // The mouse pointer is in the bottom right corner,
                    // so resizing mode is appropriate.
                    isResizing = true;
                }
                else
                {
                    // The mouse is somewhere else, so dragging mode is
                    // appropriate.
                    isDragging = true;
                }
            }
        }

        private void label_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            // Retrieve a reference to the active shape.
            HotSpot currentControl;
            currentControl = (HotSpot)sender;

            if (isDragging)
            {
                // Move the control.
                int newLeft = e.X + currentControl.Left - clickOffsetX;
                int newTop = e.Y + currentControl.Top - clickOffsetY;
                currentControl.Left = newLeft;
                currentControl.Top = newTop;
                currentControl.WallInstallation.XCoordinate = newLeft;
                currentControl.WallInstallation.YCoordinate = newTop;

                wallMarkingsChanged = true;
                saveRouteButton.Enabled = true;
                routeImageBox.Refresh();
                
            }
            else if (isResizing)
            {
                // Resize the control, according to the resize mode.
                if (currentControl.Cursor == Cursors.SizeNWSE)
                {
                    currentControl.Width = e.X;
                    currentControl.Height = e.Y;
                }
                else if (currentControl.Cursor == Cursors.SizeNS)
                {
                    currentControl.Height = e.Y;
                }
                else if (currentControl.Cursor == Cursors.SizeWE)
                {
                    currentControl.Width = e.X;
                }
            }
            else
            {
                if (currentControl.Width - e.X < EPSILON)
                {
                    if ((currentControl.Height - e.Y) < EPSILON)
                    {
                        currentControl.Cursor = Cursors.SizeNWSE;
                    }
                    else
                    {
                        currentControl.Cursor = Cursors.SizeWE;
                    }
                }
                else if (currentControl.Height - e.Y < EPSILON)
                {
                    currentControl.Cursor = Cursors.SizeNS;
                }
                else
                {
                    currentControl.Cursor = Cursors.SizeAll;
                }
            }
        }


        private void label_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            isDragging = false;
            isResizing = false;
            routeImageBox.Refresh();
        }


        private void label_MouseDoubleClick(object sender, MouseEventArgs e)
        {

            // Remove the hotspot from hotspot list and from the routeImageBox's children
            HotSpot sourceControl = (HotSpot)sender;
            routeImageBox.Controls.Remove(sourceControl);
            int i = HotspotControlsList.IndexOf(sourceControl);
            HotspotControlsList.Remove(sourceControl);

            // Remove hotspot's wall installation from route's wall installatios list
            CurrentRoute.WallInstallations.Remove(sourceControl.WallInstallation);

            // Update numbers of labels
            for (; i < HotspotControlsList.Count; i++)
            {
                int j = i + 1;
                HotspotControlsList[i].InnerText = j.ToString();
            }

            maxScoreNumericUpDown.Value = HotspotControlsList.Count;
            wallMarkingsChanged = true;
            saveRouteButton.Enabled = true;

            // Refresh the panel so that all children controls will be redrawn with the updated numbers.
            routeImageBox.Refresh();
        }


        /// <summary>
        /// Rotate the displayed video frame by 90 degrees.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rotateButton_Click(object sender, EventArgs e)
        {
            CurrentRoute.RotationAngle += 90;
            CurrentRoute.RotationAngle = (CurrentRoute.RotationAngle == 360) ? 0 : CurrentRoute.RotationAngle;
            rotateFrame();
        }


        private void drawHoldsButton_Click(object sender, EventArgs e)
        {
            // Leave this button flat (clicked appearance)
            drawHoldsButton.FlatStyle = FlatStyle.Popup;
            drawVerticalBoundariesButton.FlatStyle = FlatStyle.Standard;
            activeButton = ActiveButton.DrawHolds;
            
            // Enable \ disable buttons
            drawHoldsButton.Enabled = false;
            drawVerticalBoundariesButton.Enabled = true;
            routeImageBox.Cursor = Cursors.Cross;

            // Add event handlers for drawing hotspots
            this.routeImageBox.MouseDoubleClick += this.routeImageBoxMouseDoubleClickHandler;

            // Remove event handlers for drawing vertical boundaries
            this.routeImageBox.MouseMove -= routeImageBoxMouseMoveHandler;
            this.routeImageBox.MouseDown -= routeImageBoxMouseDownHandler;
            this.routeImageBox.MouseUp -= routeImageBoxMouseUpHandler;

        }


        private void drawVerticalBoundariesButton_Click(object sender, EventArgs e)
        {
            // Leave this button flat (clicked appearance)
            drawHoldsButton.FlatStyle = FlatStyle.Standard;
            drawVerticalBoundariesButton.FlatStyle = FlatStyle.Popup;
            activeButton = ActiveButton.DrawClips;

            // Enable \ disable buttons
            drawHoldsButton.Enabled = true;
            drawVerticalBoundariesButton.Enabled = false;
            routeImageBox.Cursor = Cursors.Cross;

            // Add event handlers for drawing vertical boundaries
            this.routeImageBox.MouseMove += routeImageBoxMouseMoveHandler;
            this.routeImageBox.MouseDown += routeImageBoxMouseDownHandler;
            this.routeImageBox.MouseUp += routeImageBoxMouseUpHandler;

            // Remove event handlers for drawing hotspots
            this.routeImageBox.MouseDoubleClick -= routeImageBoxMouseDoubleClickHandler;
        }

        private void routeImageBox_MouseDown(object sender, MouseEventArgs e)
        {
            // Verify that only two boundary lines can exist
            if (boundaryCount < 2)
            {
                shouldDrawBoundary = true;
                boundaryCount++;
                verticalBoundaryIndex = boundaryCount - 1;
            }
            else
            {
                shouldDrawBoundary = false;
                return;
            }

            // Create new hotspot object that will function as a boundary
            verticalBoundaries[verticalBoundaryIndex] = new HotSpot();
            verticalBoundaries[verticalBoundaryIndex].Size = new Size(5, routeImageBox.Height);
            verticalBoundaries[verticalBoundaryIndex].ForeColor = holdColorContentLabel.BackColor;
            verticalBoundaries[verticalBoundaryIndex].BackColor = holdColorContentLabel.BackColor;
            verticalBoundaries[verticalBoundaryIndex].Cursor = Cursors.SizeAll;
            verticalBoundaries[verticalBoundaryIndex].Location = new Point(e.Location.X, 0);
            verticalBoundaries[verticalBoundaryIndex].MouseDoubleClick += new MouseEventHandler(boundary_MouseDoubleClick);
            verticalBoundaries[verticalBoundaryIndex].MouseDown += new MouseEventHandler(boundary_MouseDown);
            verticalBoundaries[verticalBoundaryIndex].MouseMove += new MouseEventHandler(boundary_MouseMove);
            verticalBoundaries[verticalBoundaryIndex].MouseUp += new MouseEventHandler(boundary_MouseUp);
            routeImageBox.Controls.Add(verticalBoundaries[verticalBoundaryIndex]);
        }


        /// <summary>
        /// Draw the vertical boundary top to bottom as the mouse moves.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void routeImageBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (shouldDrawBoundary)
            {
                verticalBoundaries[verticalBoundaryIndex].Height = e.Y;
            }
        }


        private void routeImageBox_MouseUp(object sender, MouseEventArgs e)
        {
            shouldDrawBoundary = false;
            verticalBoundaries[verticalBoundaryIndex].Height = routeImageBox.Height;
        }

        /// <summary>
        /// Deletes the clicked boundary
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void boundary_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            // Remove the boundary from the boundaries array and from the routeImageBox's children.
            HotSpot sourceControl = (HotSpot)sender;
            routeImageBox.Controls.Remove(sourceControl);
            verticalBoundaries[verticalBoundaryIndex] = null;
            boundaryCount--;

            // Refresh the panel so that all children controls will be redrawn with the updated numbers.
            routeImageBox.Refresh();
            wallMarkingsChanged = true;
        }



        private void boundary_MouseDown(object sender, MouseEventArgs e)
        {
            shouldMoveBoundary = true;
            clickOffsetX = e.X;
        }


        /// <summary>
        /// Move the clicked boundary as the mouse drags it.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void boundary_MouseMove(object sender, MouseEventArgs e)
        {
            if (shouldMoveBoundary)
            {
                HotSpot sourceControl = (HotSpot)sender;
                sourceControl.Left = e.X + sourceControl.Left - clickOffsetX;

                // Refresh the panel so that all children controls will be redrawn with the updated numbers.
                routeImageBox.Refresh();
                wallMarkingsChanged = true;
            }

        }

        private void boundary_MouseUp(object sender, MouseEventArgs e)
        {
            shouldMoveBoundary = false;
        }

        private void browseButton_Click(object sender, EventArgs e)
        {
            FileDialog fileDialog = new OpenFileDialog();
            fileDialog.ShowDialog(this);
            picturePathTextBox.Text = fileDialog.FileName;
            if (fileDialog.FileName.Length != 0)
            {
                updateImageBoxWithFirstFrame();

                if (CurrentRoute.LiveCapture == true)
                {
                    captureFrameButton.Visible = true;
                }
                else
                {
                    captureFrameButton.Visible = false;
                    changePanelsEnabled(true);
                }
            }
        }

        private void clearButton_Click(object sender, EventArgs e)
        {
            // Clear boundaries
            clearBoundaries();
            CurrentRoute.LeftBoundary = -1;
            CurrentRoute.RightBoundary = -1;
            verticalBoundaries[0] = null;
            verticalBoundaries[1] = null;
            verticalBoundaryIndex = 0;
            boundaryCount = 0;

            // Clear hotspots
            clearHotSpots();
            CurrentRoute.WallInstallations.Clear();
            wallMarkingsChanged = true;
        }


        private void holdColorContentLabel_Click(object sender, EventArgs e)
        {
            // Show color dialog.
            ColorDialog colorDialog = new ColorDialog();
            colorDialog.ShowDialog();

            // Change label border color.
            holdColorContentLabel.BackColor = colorDialog.Color;
        }


        /// <summary>
        /// Sorts the hotspots by ascending height.  Also corrects their inner text number appropriately
        /// </summary>
        private void sortHotSpots()
        {
            HotspotControlsList.Sort(hotSpotComparer);
            for (int i = 0; i < HotspotControlsList.Count; i++)
            {
                HotspotControlsList[i].InnerText = Convert.ToString(i + 1);
            }
        }



        /// <summary>
        /// Capture the current frame again from the active camera when clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void recaptureButton_Click(object sender, EventArgs e)
        {
            routeImageBox.Controls.Remove(noImageLabel);
            routeImageBox.Image = RouteCapture.QueryFrame();
            routeImageBox.Refresh();
        }


        /// <summary>
        /// Change the editing panels' enabled field to the given boolean value.
        /// </summary>
        /// <param name="b"></param>
        private void changePanelsEnabled(bool b)
        {
            routeCardGroupBox.Enabled = b;
            routeEditingGroupBox.Enabled = b;
            routeImageBox.Enabled = b;
        }


        /// <summary>
        /// Change the Edit\Delete\Add\Select buttons enabled field to the given boolean value
        /// </summary>
        /// <param name="b"></param>
        private void changeButtonsStatus(bool b)
        {
            addRouteButton.Enabled = b;
            deleteRouteButton.Enabled = b;
            editRouteButton.Enabled = b;
            selectForTrackingButton.Enabled = b;
        }


        private void removeRouteImageBoxEventHandlers()
        {
            // Remove event handlers for drawing vertical boundaries
            this.routeImageBox.MouseMove -= routeImageBoxMouseMoveHandler;
            this.routeImageBox.MouseDown -= routeImageBoxMouseDownHandler;
            this.routeImageBox.MouseUp -= routeImageBoxMouseUpHandler;
            this.routeImageBox.MouseDoubleClick -= routeImageBoxMouseDoubleClickHandler;
        }


        internal void onTabSelected()
        {
            routesDataGridView_CellMouseClick(null, new DataGridViewCellMouseEventArgs(1, selectedRowIndex, 0, 0, new MouseEventArgs(MouseButtons.Left, 1, 0, 0, 0)));
        }

        private void generateReportButton_Click(object sender, EventArgs e)
        {
            ClimbTracker.Reports.RoutesReportForm2 repform = new ClimbTracker.Reports.RoutesReportForm2();
            repform.Show();
        }

    }

        

    public enum ActiveButton
    {
        DrawHolds, DrawClips, DrawVerticalBoundaries
    }

    // Create an IComparer for Hotspots. 
    public class HotspotComparer : IComparer<HotSpot>
    {
        // Implement the IComparable interface. 
        public int Compare(HotSpot hotspot1, HotSpot hotspot2)
        {
            int compareValue = hotspot2.Y - hotspot1.Y;
            if (compareValue > 0)
            {
                return 1;
            }
            else if (compareValue < 0)
            {
                return -1;
            }
            else
                return 0;
        }
    } 
}
